﻿using System.Collections.Generic;
using System.Xml;
using SdmxMl.Common;
using SdmxMl.Manager;
using SdmxMl.Helper;

namespace SdmxMl.Structure
{
    /// <summary>
    /// CrossSectionalMeasureType describes the observation values for multiple-measure cross-sectional 
    /// data formats. For non-cross sectional key families, it is not necesary to specify any cross-sectional 
    /// measures.The concept attribute points to the unique concept represented by the measure. 
    /// The measureDimension attribute contains the concept name of the measure dimension. The code 
    /// attribute contains the value of its corresponding code in the codelist used to represent the measure 
    /// dimension. A CrossSectionalMeasure must be declared for each code in the codelist used to represent 
    /// the measure dimension - these will replace the primary measure for multiple-measure cross-sectional 
    /// data formats.The TextFormat element allows description of the contents of the observation value. 
    /// The codelist attribute references a codelist if the observation value is coded. 
    /// If this attribute is used, then codelistAgencyID must contain the ID of the maintenance agency 
    /// of the referenced codelist. The codelistVersion attribute may be specified - if not, 
    /// then the version of the referenced codelist is assumed to be "1.0".
    /// </summary>
    public class CrossSectionalMeasureType  : BaseDimensionType
    {
        #region Properties

        /// <summary>contains the concept name of the measure dimension</summary>
        public string MeasureDimension { get; set; }
        
        /// <summary>the value of its corresponding code in the codelist used to represent 
        /// the measure dimension</summary>
        private string _code;
        public string Code
        {
            get { return _code; }
            set { _code = value; }
        }
        #endregion

        #region Constructor

        public CrossSectionalMeasureType()
            : base()
        {
            Init();
        }

        private void Init()
        {
        }

        public CrossSectionalMeasureType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr) 
            : base(nodeArtefact, nsmgr)
        {
            _code = Xmlhelper.GetAtt(nodeArtefact, "code");
            MeasureDimension = Xmlhelper.GetAtt(nodeArtefact, "measureDimension");
        }
        #endregion

        #region XML serialize

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_20)
            {
                WriteReferences(xw, prefix);
                if (string.IsNullOrEmpty(_code))
                    _code = ConceptInfo.Id;
                Xmlhelper.WriteAttributeIfExist(xw, "code", _code);
                Xmlhelper.WriteAttributeIfExist(xw, "measureDimension", MeasureDimension);

                // Text Format
                textFormat.WriteXml(xw, prefix, "TextFormat");

                // Support new SDMX 2.1 ID attribute as Annotations
                WriteAnnotationsAndId(xw, prefix);
            }
            else
            {
                WriteReferences(xw, prefix, false);
            }

            xw.WriteEndElement();
        }
        #endregion
    }

    /// <summary> Class holding the list of measures in a DSD </summary>
    public class CrossSectionalMeasureTypeList : BaseDimensionTypeList
    {
        #region Properties

        // Always one
        public PrimaryMeasureType PrimaryMeasure {get; set; }

        #endregion

        #region C'tor

        public CrossSectionalMeasureTypeList(KeyFamilyType dsd)
            : base(dsd)
        {
        }
        #endregion

        #region Redefine cross measures from codelist

        /// <summary>
        /// A CrossSectionalMeasure must be declared for each code in the codelist used to represent 
        /// the measure dimension - these will replace the primary measure for multiple-measure cross-sectional 
        /// data formats.
        /// </summary>
        /// <returns>True if created, False if unable to proceed</returns>
        public bool CreatefromMeasureCodelist()
        {
            bool created = false;

            // Old list of dimensions (no primary measure)
            List<CrossSectionalMeasureType> oldList = new List<CrossSectionalMeasureType>();
            for (int i = 1; i < this.Count; i++)
                oldList.Add(this[i] as CrossSectionalMeasureType);

            this.Clear();
            this.Add(PrimaryMeasure);
            List<DimensionType> measureList = ParentDsd.MeasureDimensionList;
            foreach (DimensionType measure in measureList)
            {
                CodeListType cl = ReferenceManager.LocateCodelist(measure.CodelistInfo);
                if (cl != null)
                {
                    // Each code
                    foreach (CodeType c in cl.CodeList)
                    {
                        CrossSectionalMeasureType cm = GetCmFromList(oldList, measure.ConceptInfo.Id, c.Id);
                        if (cm == null)
                        {
                            cm = new CrossSectionalMeasureType();
                            cm.MeasureDimension = measure.ConceptInfo.Id;
                            cm.Code = c.Id;
                        }
                        this.Add(cm);
                    }
                    created = true;
                }
            }

            return created;
        }

        // Helper
        private CrossSectionalMeasureType GetCmFromList(List<CrossSectionalMeasureType> list, string measureDim, string code)
        {
            foreach (CrossSectionalMeasureType dim in list)
                if (dim.MeasureDimension == measureDim && dim.Code == code)
                    return dim;
            return null;
        }
        #endregion

        #region Overriden methods

        public override DimensionType.DimensionKind GetKind(BaseDimensionType bd)
        {
            if (bd == PrimaryMeasure)
                return DimensionType.DimensionKind.PrimaryMeasure;

            return DimensionType.DimensionKind.Measure;
        }

        /// <summary>
        /// Get an object list representation of specified dimension (for datatable usage)
        /// </summary>
        /// <param name="bd">the base dimension to represent as objects list</param>
        /// <returns>the list</returns>
        public override List<object> GetRowObjects(BaseDimensionType bd)
        {
            List<object> lo = new List<object>(12);
            cType = ReferenceManager.LocateConcept(bd.ConceptSchemeInfo, bd.ConceptInfo.Id);
            string conceptName = cType == null ? string.Empty : cType.Name.ToString();

            clType = ReferenceManager.LocateCodelist(bd.CodelistInfo);
            string codelistName = clType == null ? string.Empty : bd.CodelistInfo.Version + " - " + clType.Name.ToString();
            lo.Add(cType != null);
            lo.Add(System.DBNull.Value); // Icon
            lo.Add(GetKind(bd));
            lo.Add(bd.ConceptInfo.Id);
            lo.Add(string.Empty);
            lo.Add(GetCrossAttachText(bd));
            lo.Add(conceptName);
            lo.Add(GetReferedCodelistId(bd));
            lo.Add(string.Empty);
            lo.Add(GetReferedTextFormat(bd));

            CrossSectionalMeasureType t = bd as CrossSectionalMeasureType;

            // If not Primary, set value
            if (t != null)
            {
                lo.Add(t.MeasureDimension);
                lo.Add(t.Code);
            }
            else
            {
                lo.Add(PrimaryMeasure.ConceptInfo.Id);
                lo.Add(string.Empty);
            }

            return lo;
        }



        public override string ToString()
        {
            return "Measures";
        }

        public override BaseDimensionType NewItem()
        {
            
            CrossSectionalMeasureType cm =  new CrossSectionalMeasureType();
            if (ParentDsd != null)
            {
                // Add measure dimension ref (support only one)
                List<DimensionType> listMeas = ParentDsd.DimensionList.GetMeasureDimensions();
                if (listMeas.Count > 0)
                    cm.MeasureDimension = listMeas[0].ConceptInfo.Id;
            }
            return cm;
        }
        #endregion
    }

}
